#!/usr/bin/python
# -*- coding: UTF-8 -*-

import os
import xlrd
import numpy as np
import json
import sklearn.cluster as skc
import matplotlib.pyplot as plt
import matplotlib.patches as mpathes
from tkinter import *
from tkinter import filedialog

"""
废弃斑马线报警纠错
"""
class AlarmCorrection(Frame):

    filename = ''
    areas = []

    def __init__(self, master=None):
        Frame.__init__(self, master)
        self.pack()
        self.create_widgets()

    def create_widgets(self):
        """
        初始化窗体
        :return:
        """
        self.disableLabel = Label(self, text="请选择废弃斑马线报警：")
        self.enableLabel  = Label(self, text="请选择有效斑马线报警：")
        self.exportLabel = Label(self, text="导出误报范围")
        self.disable_convertButton = Button(self, text="选择文件", command=self.analysis_disable_points)
        self.enable_convertButton = Button(self, text="选择文件", command=self.analysis_enable_points)
        self.export_button = Button(self, text="点击导出", command=self.export_disable_area)

        self.disableLabel.grid(row=1, column=0)
        self.disable_convertButton.grid(row=1, column=1)
        self.enableLabel.grid(row=2, column=0)
        self.enable_convertButton.grid(row=2, column=1)
        self.exportLabel.grid(row=3, column=0)
        self.export_button.grid(row=3, column=1)


    def export_disable_area(self):
        """
        导出围栏列表
        :return:
        """
        (filename, exten) = os.path.splitext(self.filename)
        outputfile = filename + '.json'
        output = open(outputfile, 'w+', buffering=2048)
        # 写json文件
        output.write('[\n')
        for i in range(len(self.areas)):
            temp = ''
            for j in range(len(self.areas[i])):
                temp = temp + "%s," % json.dumps(self.areas[i][j])
                # 去除最后一个逗号
            temp = temp[:-1]
            row = ' [%s],' % temp
            if i == (len(self.areas)-1):
                # 最后一行需要删除逗号
                row = row[:-1]
            output.write(row)
            output.write('\n')
        # 写结尾']'
        output.write(']\n')
        output.close()
        print("导出完成")

    def analysis_enable_points(self):
        """
        有效斑马线报警不需要解析聚集关系，只需要在图中画出即可
        """
        fd = filedialog.LoadFileDialog(self)
        filename = fd.go()

        if filename:
            dataset = self.get_dataset_from_excel(filename)
            col = [0, 0.5, 1, 0.7]
            plt.plot(dataset[:, 0], dataset[:, 1], 'o', markerfacecolor=tuple(col),markeredgecolor='k', markersize=6)

    def analysis_disable_points(self):
        """
        分析误报点
        :return:
        """
        fd = filedialog.LoadFileDialog(self)
        filename = fd.go()

        if filename:
            self.filename = filename
            self.do_analysis_cluster(filename)

    def get_dataset_from_excel(self, filename):
        """
        从excel文件中读取位置数据
        :param filename:
        :return: 二维数组: [[经度1, 纬度1], [经度2, 纬度2], ...]
        """
        excel_file = xlrd.open_workbook(filename)

        table = excel_file.sheet_by_index(0)
        nrows = table.nrows
        ncols = table.ncols
        title_table = table.row_values(0)

        labelset = []
        dataset = []

        # 写开头格式
        for i in range(1, nrows):
            gps = []
            for j in range(ncols):
                if 'longitude' == title_table[j]:
                    gps.append(float(table.row(i)[j].value))
                elif 'latitude' == title_table[j]:
                    gps.append(float(table.row(i)[j].value))
                elif 'parent_org_name' == title_table[j]:
                    # 获取数据类别
                    labelset.append(table.row(i)[j].value)
            dataset.append(gps)

        dataset = np.array(list(set([tuple(t) for t in dataset])))
        return dataset


    def do_analysis_cluster(self, filename):

        dataset = self.get_dataset_from_excel(filename)

        db = skc.DBSCAN(eps=0.0002, min_samples=5).fit(dataset)
        core_samples_mask = np.zeros_like(db.labels_, dtype=bool)  # 设置一个样本个数长度的全false向量
        core_samples_mask[db.core_sample_indices_] = True #将核心样本部分设置为true
        labels = db.labels_

        # 获取聚类个数。（聚类结果中-1表示没有聚类为离散点）
        n_clusters_ = len(set(labels)) - (1 if -1 in labels else 0)

        # 使用黑色标注离散点
        unique_labels = set(labels)
        # colors = [plt.cm.Spectral(each) for each in np.linspace(0, 1, len(unique_labels))]
        col = [1, 0, 0, 0.9]
        fig,ax = plt.subplots()
        plt.rcParams['font.sans-serif']=['SimHei']
        plt.rcParams['axes.unicode_minus'] = False
        plt.title('废弃斑马线画框数量: %d' % n_clusters_)

        # for k, col in zip(unique_labels, colors):
        for k in zip(unique_labels):

            class_member_mask = (labels == k)  # 将所有属于该聚类的样本位置置为true

            xy = dataset[class_member_mask & core_samples_mask]  # 将所有属于该类的核心样本取出，使用大图标绘制
            plt.plot(xy[:, 0], xy[:, 1], 'o', markerfacecolor=tuple(col),markeredgecolor='k', markersize=14)

            # 画框
            x = np.sort(xy[:,0])
            y = np.sort(xy[:,1])
            # Rectangle(xy2,0.2,0.1,color='r')
            if len(xy) > 0:
                leftBottomPoint = np.array([x[0], y[0]])
                width = x[-1] - x[0]
                height = y[-1] - y[0]
                rect = mpathes.Rectangle(leftBottomPoint, width, height, color='r')
                ax.add_patch(rect)

                #统计围栏范围
                left_top = {'longitude':x[0], 'latitude':y[-1]}
                right_top = {'longitude':x[-1], 'latitude':y[-1]}
                right_bottom = {'longitude':x[-1], 'latitude':y[0]}
                left_bottom = {'longitude':x[0], 'latitude':y[0]}
                area_points = [left_top, right_top, right_bottom, left_bottom, left_top]
                self.areas.append(area_points)
            xy = dataset[class_member_mask & ~core_samples_mask]  # 将所有属于该类的非核心样本取出，使用小图标绘制
            plt.plot(xy[:, 0], xy[:, 1], 'o', markerfacecolor=tuple(col),markeredgecolor='k', markersize=6)

        plt.show()

app = AlarmCorrection()
# 设置窗口标题:
app.master.title('废弃斑马线误报分析')
# 主消息循环:
app.mainloop()
